/**
 * @Author       : Mo 2427995270@qq.com
 * @Date         : 2023-03-14 15:30:32
 * @LastEditTime : 2024-01-26 15:37:35
 * @FilePath     : \KT-2020-CT\libs\move_average.c
 * @Description  : 移动平均算法库 wma(加权) hma(赫本)
 * @Version      : V1.0
 * @History      : 后缀d代表双精度类型
 *
 * @Note         : in缓冲区是循环的，存满后新数据会覆盖旧数据。end_idx是新数据 start_idx是最旧数据
 * 					如不特殊说明，out缓冲区不是循环的，最旧数据在索引0 最新数据在out_idx-1；
 * 					sma ema的out缓冲区是循环的
 */
/***************************************Includes***********************************/
#include "move_average.h"
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
// #include <string.h>

/************************************Private Macros********************************/
// #define


/**********************************Private Statement*******************************/



/***********************************Public Variables*******************************/




/**********************************Private Variables*******************************/



/**********************************Public Functions********************************/

/**
 * @brief 初始化结构
 * @param  type
 * @param  quantity                平均窗口大小
 * @return int 0 成功  -1 失败
 * @note 使用该函数初始化，在不再使用结构后请调用move_ave_wma_d_free()释放资源
 */
int move_ave_d_init(move_ave_d_t *type, int quantity) {
    type->quantity = quantity;
    type->in = malloc(quantity * sizeof(double));
    type->start_idx = 0;
    type->end_idx = 0;
    type->out_idx = 0;
    type->_last_result = 0;
    type->_series = 1;
    if (!type->in)
        return -1;

    type->out = malloc(quantity * sizeof(double));
    if (type->out)
        return 0;

    free(type->in);
    return -1;
}
/**
 * @brief 释放结构成员占用的资源
 * @param  type
 * @note null
 */
void move_ave_d_free(move_ave_d_t *type) {
    free(type->in);
    free(type->out);
}
/**
 * @brief 添加新值
 * @param  type
 * @param  new              新值
 * @return int 0 添加成功  -1 失败
 * @note 首次添加，则整个in缓冲区将被该值覆盖，指数移动平均不需要使用这个函数
 */
int move_ave_d_add(move_ave_d_t *type, double new) {
    if (!type || !type->in || !type->out)
        return -1;
    // 首次添加
    if (type->start_idx == type->end_idx) {
        for (int i = 0; i < type->quantity; i++) {
            type->in[i] = new;
        }
        type->start_idx = 0;
        type->end_idx = type->quantity - 1;
        return 0;
    }
    // 非首次添加
    ++type->end_idx;
    if (type->end_idx >= type->quantity)
        type->end_idx = 0;
    type->in[type->end_idx] = new;
    // printf("add %lf  %d\n", type->in[type->end_idx], type->end_idx);
    type->start_idx = type->end_idx + 1;
    if (type->start_idx >= type->quantity)
        type->start_idx = 0;
    return 0;
}
/**
 * @brief 计算简单移动平均
 * @param  type
 * @param  T                窗口大小 最大等于 quantity
 * @param  result           计算结果
 * @return int
 * @note null
 */
int move_ave_sma_d(move_ave_d_t *type, int T, double *result) {
    if (!type || !type->in || !type->out)
        return -1;
    if (T > type->quantity)
        T = type->quantity;
    double sum = 0;
    int idx = type->start_idx;
    // 复制前 T-1 个原始值到输出缓冲区 T-1之后的输出值为均值
    type->out_idx = 0;
    for (int i = 1; i < T; i++) {
        type->out[type->out_idx++] = type->in[idx++];
        if (idx >= type->quantity)
            idx = 0;
    }
    idx = type->start_idx;
    // 计算前 T-1 个加和
    for (int i = 1; i < T; i++) {
        sum += type->in[idx++];
        if (idx >= type->quantity)
            idx = 0;
    }
    int sub_idx = type->start_idx;
    // 计算第 T-1 到 end_idx 的加权和 和输出结果到输出缓冲区
    for (int i = T - 1; i < type->quantity; i++) {
        sum += type->in[idx++];
        type->out[type->out_idx++] = sum / T;

        sum -= type->in[sub_idx++];

        if (idx >= type->quantity)
            idx = 0;
        if (sub_idx >= type->quantity)
            sub_idx = 0;
    }
    if (result) {
        *result = type->out[type->out_idx - 1];
    }
    return 0;
}
/**
 * @brief 计算加权移动平均
 * @param  type
 * @param  T                窗口大小 最大等于 quantity
 * @param  result           计算结果
 * @return int 0 计算成功  -1 失败
 * @note null
 */
int move_ave_wma_d(move_ave_d_t *type, int T, double *result) {
    if (!type || !type->in || !type->out)
        return -1;
    if (T > type->quantity)
        T = type->quantity;

    int divider = T * (T + 1) / 2;
    double sum = 0, sub = 0;
    int idx = type->start_idx;
    // 复制前 T-1 个原始值到输出缓冲区 T-1之后的输出值为加权均值
    type->out_idx = 0;
    for (int i = 1; i < T; i++) {
        type->out[type->out_idx++] = type->in[idx++];
        if (idx >= type->quantity)
            idx = 0;
    }
    idx = type->start_idx;
    // 计算前 T-1 个加权和
    for (int i = 1; i < T; i++) {
        sub += type->in[idx];
        sum += i * type->in[idx++];
        if (idx >= type->quantity)
            idx = 0;
    }
    int sub_idx = type->start_idx;
    double sub_value = 0.0;
    // 计算第 T-1 到 end_idx 的加权和 和输出结果到输出缓冲区
    for (int i = T - 1; i < type->quantity; i++) {
        sub += type->in[idx];
        sub -= sub_value;
        sum += type->in[idx++] * T;

        sub_value = type->in[sub_idx++];
        type->out[type->out_idx++] = sum / divider;

        sum -= sub;

        if (idx >= type->quantity)
            idx = 0;
        if (sub_idx >= type->quantity)
            sub_idx = 0;
    }
    if (result) {
        *result = type->out[type->out_idx - 1];
    }
    return 0;
}
/**
 * @brief 计算指数移动平均(修正版)
 * @param  type
 * @param  wf               权重因子 一般接近1 等于1new将不参与本次计算，直至下次
 * @param  new              t时刻的值
 * @param  result           计算结果
 * @return int
 * @note wf越大 曲线越平滑瞬时响应弱，wf越小 瞬时响应强 曲线不稳
 */
int move_ave_ema_d(move_ave_d_t *type, float wf, double new, double *result) {
    if (!type || !type->in || !type->out)
        return -1;
    // 保存t时刻值
    ++type->end_idx;
    if (type->end_idx >= type->quantity)
        type->end_idx = 0;
    type->in[type->end_idx] = new;
    type->start_idx = type->end_idx + 1;

    // 计算均值
    if (type->out_idx >= type->quantity)
        type->out_idx = 0;
    type->out[type->out_idx] = type->_last_result * wf + (1 - wf) * new;
    type->_last_result = type->out[type->out_idx++];
    // 修正
    if (type->_series > 0.001) {
        // 计算修正无穷级数
        type->_series *= wf;
        type->out[type->out_idx - 1] /= (1 - type->_series);
    }
    if (result) {
        *result = type->out[type->out_idx - 1];
    }
    return 0;
}
/**
 * @brief 计算分形自适应移动平均
 * @param  type
 * @param  new              t时刻的值
 * @param  result           计算结果
 * @return int
 * @note 使用整个in缓冲区大小为T，在数据还没填充整个in缓冲区时均值输出为new
 */
int move_ave_frama_d(move_ave_d_t *type, double new, double *result) {
    if (!type || !type->in || !type->out)
        return -1;
    // 首次填充in
    if (type->start_idx == type->end_idx) {
        type->in[type->end_idx] = new;
        type->start_idx = ++type->end_idx;
        if (type->end_idx == type->quantity) {
            type->end_idx--;
            type->start_idx = 0;
        }
        // printf("add %lf  idx %d\n", new, type->end_idx);
        type->out[type->out_idx++] = new;
        return 0;
    }
    // t-T ~ t-2T+1
    int T = type->quantity / 2;
    double w2_max = type->in[type->start_idx];
    double w2_min = type->in[type->start_idx];
    int w2_idx = type->start_idx + 1;
    for (int i = 1; i < T; i++) {
        if (type->in[w2_idx] > w2_max)
            w2_max = type->in[w2_idx++];
        else if (type->in[w2_idx] < w2_min)
            w2_min = type->in[w2_idx++];
        if (w2_idx >= type->quantity)
            w2_idx = 0;
    }
    double n2 = (w2_max - w2_min) / T;
    // t ~ t-T+1
    int w1_idx = type->start_idx;
    for (int i = 0; i < T; i++) {
        w1_idx++;
        if (w1_idx >= type->quantity)
            w1_idx = 0;
    }
    double w1_max = type->in[w1_idx];
    double w1_min = type->in[w1_idx];
    w1_idx++;
    for (int i = 1; i < T; i++) {
        if (type->in[w1_idx] > w1_max)
            w1_max = type->in[w1_idx++];
        else if (type->in[w1_idx] < w1_min)
            w1_min = type->in[w1_idx++];
        if (w1_idx >= type->quantity)
            w1_idx = 0;
    }
    double n1 = (w1_max - w1_min) / T;
    // t ~ t-2T+1
    int w_idx = type->start_idx;
    double w_max = type->in[w_idx];
    double w_min = type->in[w_idx++];
    for (int i = 1; i < type->quantity; i++) {
        if (type->in[w_idx] > w_max)
            w_max = type->in[w_idx++];
        else if (type->in[w_idx] < w_min)
            w_min = type->in[w_idx++];
        if (w_idx >= type->quantity)
            w_idx = 0;
    }
    double n3 = (w_max - w_min) / type->quantity;
    // 计算分形维数
    double D = (log(n1 + n2) - log(n3)) / 0.6931; // log(2) == 0.6931
    double wf = 1 - exp(-4.6 * (D - 1));
    // printf("wf %lf  D %lf T %d W1 %lf %lf  W2 %lf %lf W %lf %lf\n", wf, D, T, w1_max, w1_min, w2_max, w2_min, w_max, w_min);
    move_ave_ema_d(type, wf, new, NULL);
    if (result) {
        *result = type->out[type->out_idx - 1];
    }
    return 0;
}

/**
 * @brief 计算赫尔移动平均
 * @param  type
 * @param  T                窗口大小 最大等于 quantity
 * @param  result           计算结果
 * @return int 0 计算成功  -1 失败
 * @note null
 */
int move_ave_hma_d(move_ave_d_t *type, int T, double *result) {
    if (!type || !type->in || !type->out)
        goto __exit_err;
    if (T > type->quantity)
        T = type->quantity;
    double *original_out = type->out;
    double *original_in = type->in;
    double *wma_in = malloc(type->quantity * sizeof(double));
    if (!wma_in)
        goto __exit_err;
    double *wma_1 = malloc(type->quantity * sizeof(double));
    if (!wma_1)
        goto __exit_wma_in;
    double *wma_2 = malloc(type->quantity * sizeof(double));
    if (!wma_2)
        goto __exit_wma_1;

    type->out = wma_1;
    move_ave_wma_d(type, (int)(T / 2.0 + 0.5), NULL);
    type->out = wma_2;
    move_ave_wma_d(type, T, NULL);
    type->in = wma_in;
    for (int i = 0; i < type->quantity; i++) {
        wma_in[i] = 2 * wma_1[i] - wma_2[i];
    }
    type->out = original_out;
    move_ave_wma_d(type, (int)(sqrt(T) + 0.5), NULL);
    type->in = original_in;

    free(wma_in);
    free(wma_1);
    free(wma_2);

    if (result) {
        *result = type->out[type->out_idx - 1];
    }

    return 0;
__exit_wma_1:
    free(wma_1);
__exit_wma_in:
    free(wma_in);
__exit_err:
    return -1;
}
/**
 * @brief 返回计算结果
 * @param  type
 * @return double
 * @note null
 */
double move_ave_result(move_ave_d_t *type) {
    return type->out[type->out_idx - 1];
}

/**********************************Private Functions*******************************/


/* [] END OF FILE */
